#include "R5.H"

void interrupt l1_int () {

	int iid_reg;

	if (serial.state != OPEN) {
		outportb(0x20,0x20);
		return;
	}

	iid_reg = inportb(COM1_INT_ID_REG);
	iid_reg = iid_reg & 0x07;
	if (iid_reg == 2) output_handler();
	if (iid_reg == 4) input_handler();

	outportb(0x20,0x20);

}

void input_handler () {

	char buf;
	buf = inportb(COM1_BASE);

	if (serial.status_code == READING) {

		if (buf != '\r' && serial.in_done < *serial.in_count) {

			serial.in_buff[serial.in_done]=buf;
			serial.in_done++;

		} else if (buf == '\r' || serial.in_done == *serial.in_count) {

			serial.in_buff[serial.in_done]='\0';
			serial.status_code=IDLE;
			*serial.event_flag=1;
			*serial.in_count=serial.in_done;

		}

	} else {

		if (serial.ring_buffer_count < RING_BUFFER_SIZE) {
			serial.ring_buffer[serial.ring_buffer_in]=buf;
			serial.ring_buffer_in++;
			serial.ring_buffer_count++;
			return;
		} else return;

	}
}

void output_handler() {

	unsigned char mask;

	if (serial.status_code != WRITING) return;

	//if (serial.out_done < *serial.out_count) {


	if (serial.out_done < *serial.out_count) {
		outportb(COM1_BASE, serial.out_buff[serial.out_done]);
		serial.out_done++;
		return;

	}
		//return;

	//}

	serial.status_code=IDLE;
	*serial.event_flag=1;
	disable();
	mask = inportb(COM1_INT_EN);
	mask = mask & ~0x02;
	outportb(COM1_INT_EN, mask);

}

int com_write(char * buf_p, int *count_p) {

	unsigned char mask;

	if (serial.state != OPEN) return -401; // port not open WHY NOT USE SAME ERROR CODE
	if (buf_p == NULL) return -402;
	if (count_p == NULL || *count_p == 0) return -403;
	if (serial.status_code != IDLE) return -404;

	serial.out_buff = buf_p;
	serial.out_count = count_p;
	serial.out_done = 0;
	serial.status_code = WRITING;

	*serial.event_flag = 0;
	
	outportb(COM1_BASE, *serial.out_buff);
	serial.out_done++;

	disable();
	mask = inportb(COM1_INT_EN);
	mask = mask | 0x02;
	outportb(COM1_INT_EN,mask);
	enable();
	
	return 0;

}

// COM Functions

int com_open(int *eflag_p, int baud_rate) {

	int brd;
	unsigned char mask;

	if (eflag_p == NULL) return INVALID_EVEN_FLAG_POINTER;
	if (baud_rate < 0) return INVALID_BAUD_RATE_DIVISOR;
	if (serial.state == OPEN) return PORT_ALREADY_OPEN;
	
	serial.state = OPEN;
	serial.event_flag = eflag_p;
	serial.status_code = IDLE;

	serial.ring_buffer_in=serial.ring_buffer_out=serial.ring_buffer_count=0;
	
	saved_interrupt = getvect(COM1_INT_ID);
	
	disable();
	
	setvect(COM1_INT_ID, l1_int);
	brd = 115200 / (long) baud_rate;

	outportb(COM1_LC, 0x80);
	outportb(COM1_BRD_LSB, brd&0xFF);
	outportb(COM1_BRD_MSB, (brd >> 8) & 0xFF);
	outportb(COM1_LC, 0x03);
	
	mask = inportb(PIC_MASK);
	mask = mask & ~0x10;
	outportb (PIC_MASK, mask);
	outportb(COM1_MC, 0x08);
	outportb(COM1_INT_EN, 0x01);
	
	enable();
	
	return 0;

}

int com_close(void) {

	int mask;

	if (serial.state != OPEN) return PORT_NOT_OPEN;
	
	serial.state = CLOSED;
	
	disable();
	
		mask = inportb(PIC_MASK);
		mask = mask | 0x80; //something FIX ME
		outportb(PIC_MASK, mask);
		outportb(COM1_MS, 0x00);
		outportb(COM1_INT_EN, 0x00);
		setvect(COM1_INT_ID, saved_interrupt);
	
	enable();
	return 0;

}

int com_read(char *buf_p, int *count_p) {

	char tmp;

	if (serial.state == CLOSED) return PORT_NOT_OPEN;
	if (serial.status_code != IDLE) return PORT_BUSY;
	if (count_p == NULL) return INVALID_COUNT_ADDRESS_OR_VALUE;
	if (buf_p == NULL) return INVALID_BUFFER_ADDRESS;

	serial.in_buff = buf_p;
	serial.in_count = count_p;
	serial.in_done = 0;
	*serial.event_flag=0;
	
	disable();
	
	serial.status_code = READING;
	
	if (serial.ring_buffer_count > 0)
		while (serial.in_done < *serial.in_count && serial.ring_buffer_count > 0 && serial.ring_buffer[serial.ring_buffer_out] != '\r') {

			serial.in_buff[serial.in_done]=serial.ring_buffer[serial.ring_buffer_out];
			serial.ring_buffer_out+=1;
	
		}
	
	enable();
	
	if (serial.in_done < *serial.in_count) return 0;
	
	serial.in_buff[*serial.in_count-1]='\0';
	serial.status_code=IDLE;
	*serial.event_flag = 1;
	
	return serial.in_done;

}